﻿using System;
using System.Collections.Generic;
using System.Text;
using IM;
using System.Threading;
using System.Net;
using System.Net.Mail;
using System.Timers;
using System.Windows.Forms;
using System.IO;
using System.Xml.Serialization;



namespace HistoryToMail
{
    public delegate void LoggerEvent(String textToLogger);

    public class MailSender
    {
        public class ReadyToSend
        {
            public String Subject;
            public String Body;
            public String Sender;

            [System.Xml.Serialization.XmlIgnoreAttribute]
            public Boolean wasSend = false;
        }


        private SMTPSettings settings;
        private List<ReadyToSend> messages = new List<ReadyToSend>();

        private AutoResetEvent newMessageIsAvailable = new AutoResetEvent(false);
        private AutoResetEvent stopThread = new AutoResetEvent(false);
        private Thread sendThread;

        public MailSender(ref SMTPSettings settings)
        {
            this.settings = settings;
            Start();
            LoadMessages();
        }

        ~MailSender()
        {
            Stop();
        }


        public event LoggerEvent LoggerOut;

        public void changeSettings(ref SMTPSettings settings)
        {
            this.settings = settings;
        }

        private void Start()
        {
            Stop();
            sendThread = new Thread(SendThread);
            sendThread.Start();
        }

        public void Stop()
        {
            if (sendThread != null)
            {
                stopThread.Set();
                // To do: wait while thread will stop
                sendThread.Join();
                sendThread = null;
                if (messages.Count != 0)
                {
                    if (MessageBox.Show("Не получилось отправить " + messages.Count + " цепочек сообщений.\r\n Сохранить их локально и отправить при следующем запуске QIP?", "Ошибка отправки...", MessageBoxButtons.YesNo, MessageBoxIcon.Error)
                        == DialogResult.Yes)
                    {
                        SaveMessages(); 
                    }
                }
            }
        }

        public void LoadMessages()
        {
            try
            {
                System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(messages.GetType());
                TextReader input = new StreamReader("unsaved.dat");
                messages = (List<ReadyToSend>) x.Deserialize(input);
                input.Close();
                if (messages.Count > 0)
                {
                    newMessageIsAvailable.Set();
                }
                System.IO.File.Delete("unsaved.dat");
            }
            catch (Exception)
            {
            }
        }

        public void SaveMessages()
        {
            System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(messages.GetType());
            //StringWriter Output = new StringWriter(new StringBuilder());
            TextWriter Output = new StreamWriter("unsaved.dat");
            x.Serialize(Output, messages);
            //String data = Output.ToString();
            Output.Close();
        }

        public int GetQueueLength()
        {
            return messages.Count;
        }

        public void AddMessageToSend(MessageToSend msg)
        {
            ReadyToSend rmsg = new ReadyToSend();
            if (LoggerOut != null)
            {
                LoggerOut("================================");
                LoggerOut("        AddMessageToSend        ");
                LoggerOut(" Subject " + msg.Subject);
                LoggerOut(" Body    " + msg.Body);
                LoggerOut("================================");
            }
            rmsg.Subject = msg.Subject;
            rmsg.Body = msg.Body;
            rmsg.Sender = msg.Sender;
            lock (messages)
            {
                messages.Add(rmsg);
            }
            newMessageIsAvailable.Set();
        }

        private void OnTimedEvent(object source, ElapsedEventArgs e)
        {
            if (LoggerOut != null)
            {
                LoggerOut("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
                LoggerOut("   SendThread: repeat send after error!!!!");
                LoggerOut("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
            }
            newMessageIsAvailable.Set();
        }

        private void SendThread()
        {
            Boolean exitWhenNoData = false;
            System.Timers.Timer timer = new System.Timers.Timer(30000);
            timer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
            while (true)
            {
                int res = WaitHandle.WaitAny(new WaitHandle[] { newMessageIsAvailable, stopThread });
                switch (res)
                {
                    case 0:
                        {
                            if (timer.Enabled)
                            {
                                timer.Stop();
                            }

                            int messageIndex = 0;
                            while (true)
                            {
                                ReadyToSend msg = null;

                                lock (messages)
                                {
                                    if (messages.Count == 0)
                                    {
                                        if (exitWhenNoData)
                                        {
                                            return; //Thread ready to stop
                                        }
                                        break;
                                    }
                                    if (messageIndex < messages.Count)
                                    {
                                        msg = messages[messageIndex];
                                    }
                                }
                                if (msg != null)
                                {
                                    SendMessage(msg);
                                    if (msg.wasSend)
                                    {
                                        lock (messages)
                                        {
                                            messages.RemoveAt(messageIndex);
                                        }
                                    }
                                    else
                                    {

                                    }
                                }
                                else
                                {
                                    if (exitWhenNoData)
                                    {
                                        return;
                                    }

                                    if (messages.Count != 0)
                                    {
                                        timer.Stop();
                                        timer.Start();
                                    }
                                    break;
                                }
                                messageIndex++;
                            }
                        } break;
                    case 1:
                        {
                            exitWhenNoData = true;
                            newMessageIsAvailable.Set();
                        }break;
                }
            }
        }

        private void SendMessage(ReadyToSend msg)
        {
            try
            {
                msg.wasSend = false;
                SmtpClient smtp = new SmtpClient(settings.serverAddress, settings.serverPort);
                if (settings.serverUseAuth)
                {
                    smtp.UseDefaultCredentials = false;
                    NetworkCredential SMTPUserInfo = new NetworkCredential(settings.serverUserName, settings.serverPassword);
                    smtp.Credentials = SMTPUserInfo;
                }
                else
                {
                    smtp.Credentials = CredentialCache.DefaultNetworkCredentials;
                    smtp.UseDefaultCredentials = true;
                }
                smtp.EnableSsl = settings.serverUseSSL;

                if (LoggerOut != null)
                {
                    LoggerOut("==============================================");
                    LoggerOut("     SendThread");
                    LoggerOut("settings.senderAddress      " + settings.senderAddress);
                    LoggerOut("settings.destinationAddress " + settings.destinationAddress);
                    LoggerOut("msg.Subject                 " + msg.Subject);
                    LoggerOut("msg.Body                    " + msg.Body);
                    LoggerOut("==============================================");
                }

                MailMessage message = new MailMessage(msg.Sender + "<" + settings.senderAddress + ">", "test receiver<" + settings.destinationAddress + ">", msg.Subject, msg.Body);
                message.IsBodyHtml = true;
                smtp.Send(message);
                msg.wasSend = true;
            }
            catch (Exception exception)
            {
                if (LoggerOut != null)
                {
                    LoggerOut("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
                    LoggerOut(exception.ToString());
                    LoggerOut("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
                }
            }
        }
    }
}
